/*
 * Copyright (c) 2010 CCX(China) Co.,Ltd. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of
 * CCX(China) Co.,Ltd. ("Confidential Information").
 * It may not be copied or reproduced in any manner without the express 
 * written permission of CCX(China) Co.,Ltd.
 *
 * Author: zhnb
 * Date: 2011-4-11 下午01:32:14
 */

package com.dbpool;

import java.io.FileNotFoundException;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.dom4j.DocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jolbox.bonecp.BoneCP;
import com.jolbox.bonecp.BoneCPConfig;

/**
 * 数据库连接池集工具类
 * @author zhnb
 *
 */
public class BoneCPsUtil implements Serializable{
	
	private static final long serialVersionUID = 6891270510723662780L;
	private static Logger logger = LoggerFactory.getLogger(BoneCPsUtil.class);

	//数据源配置信息集<configInfo>
	private List<ConfigInfo> confList = null;
	
	//数据池集合<key_dataSourceName, value_connectionPool>
	private Map<String, BoneCP> poolsMap = new HashMap<String, BoneCP>(3);
	public Map<String, BoneCP> getPoolsMap() {
		return poolsMap;
	}
	
	/**
	 * 初始化多数据源连接池
	 * @throws Exception 
	 */
	public BoneCPsUtil() throws Exception{
		this( null );
	}

	/**
	 * 初始化多数据源连接池
	 * @param confile		配置文件（默认为bonecp-config.xml，在src目录下。）
	 * @throws Exception 
	 */
	public BoneCPsUtil( String confile ) throws Exception{
		
		//实例化信息集
		this.initConfList( confile );
		//实例化池集
		this.initPoolsMap();
		
	}
	
	/**
	 * 根据配置文件，实例化信息集。
	 * @param conffile				配置文件名
	 * @throws FileNotFoundException
	 * @throws DocumentException
	 */
	private void initConfList( String conffile ) throws FileNotFoundException, DocumentException{

		//提取配置信息
		ConfigLoader loader = new ConfigLoader();
		
		//如果未指定配置文件，则使用默认。
		if ( conffile == null ) {
			this.confList = loader.getConfigInfoList();
		}
		//指定配置文件
		else {
			this.confList = loader.getConfigInfoList( conffile );
		}
	}

	/**
	 * 用取到的配置信息，实例化池集。
	 * @throws Exception 
	 * @throws Exception 
	 */
	private void initPoolsMap() throws Exception {
		
		//如果信息集为空，直接抛出异常。
		if ( this.confList == null || this.confList.size() < 1 ) {
			throw new Exception("BoneCP Configuration failure, cause by can not get config information.");
		}
		
		//根据池信息集，填充池集。
		for (ConfigInfo confInfo : confList) {
			
			//加载相关驱动
			Class.forName( confInfo.getJdbcClass() );
			
			//请求数据库连接类
			BoneCPConfig config = new BoneCPConfig();
			
			config.setJdbcUrl( confInfo.getJdbcUrl() );
			config.setUsername( confInfo.getUsername() ); 
			config.setPassword( confInfo.getPassword() );
			config.setMinConnectionsPerPartition( confInfo.getMinConnectionsPerPartition() );
			config.setMaxConnectionsPerPartition( confInfo.getMaxConnectionsPerPartition() );
			config.setPartitionCount( confInfo.getPartitionCount() );
			
			//创建连接池
			BoneCP connectionPool = null;
			try {
				
				//如果连接无效直接跳过
				connectionPool = new BoneCP(config);
				
				//添加超时读取数据源(暂不允许使用)
				//TODO: 1_bcp, set property 'timeout' in 'bonecp-config.xml'
				//connectionPool = this.getBoneCPinTime(config, 300);
				
				//添加连接池到池集
				this.poolsMap.put( confInfo.getDataSourceName(), connectionPool );
			} catch (Exception e) {
				logger.info("bad source:" + confInfo.getDataSourceName());
				logger.debug( e.getStackTrace().toString() );
			}
			
		}
	}
	
	/**
	 * 强制在超时时间内完成数据源池初始化。
	 * 若指定时间内未完成，则抛弃当前初始化工作 返回NULL，并转入下一个可能的池初始化工作。
	 * @param config			数据源池配置信息
	 * @param timeout			超时时间
	 * @return					数据源对应连接池
	 */
	@SuppressWarnings({ "unchecked", "unused" })
	private BoneCP getBoneCPinTime( final BoneCPConfig config, long timeout ){
		
		BoneCP bcp = null;

		//创建一个单线程执行者
		ExecutorService executor = Executors.newSingleThreadExecutor();
		//创建一个将执行的任务
		FutureTask<BoneCP> future = (FutureTask<BoneCP>) executor.submit(new Callable() {
			public BoneCP call() {
				BoneCP bcpT = null;
				try {
					bcpT = new BoneCP( config );
				} catch (SQLException e) {
					e.printStackTrace();
				}

				return bcpT;
			}
		});
		//将此任务添加至些单线程执行者
		executor.execute(future);

		try {
			//执行指定超时的调用
			bcp = future.get( timeout, TimeUnit.MILLISECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		} catch (TimeoutException e) {
			e.printStackTrace();
		} finally {
			executor.shutdown();
		}

		return bcp;
	}

}
